use std::hash::{Hash, Hasher};
use axum::{extract::Path, http::StatusCode, Extension};
use axum::http::{HeaderMap, HeaderValue};
use bytes::Bytes;
use anyhow::Result;
use std::collections::hash_map::DefaultHasher;
use axum::extract::State;
use crate::model::Params;
use percent_encoding::percent_decode_str;
use tracing::{info, instrument};
use crate::Cache;
use crate::protocol::ImageSpec;

pub(crate) async fn generate(Path(Params { spec, url }): Path<Params>, Extension(cache): Extension<Cache>) -> Result<(HeaderMap, Vec<u8>), StatusCode> {
    let spec: ImageSpec = spec.as_str().try_into().map_err(|_| StatusCode::BAD_REQUEST)?;
    let url: &str = &percent_decode_str(&url).decode_utf8_lossy();

    let data = retrieve_image(&url, cache).await.map_err(|_| StatusCode::BAD_REQUEST)?;

    let mut headers = HeaderMap::new();
    headers.insert("Content-Type", HeaderValue::from_static("image/jpeg"));
    Ok((headers, data.to_vec()))
}

#[instrument(level = "info", skip(cache))]
async fn retrieve_image(url: &str, cache: Cache) -> Result<Bytes> {
    let mut hasher = DefaultHasher::default();
    url.hash(&mut hasher);
    let key = hasher.finish();
    let g = &mut cache.lock().await;
    let data = match g.get(&key) {
        Some(v) => {
            info!("Match cache {}", key);
            v.to_owned()
        }
        None => {
            info!("Retrieve url");
            let resp = reqwest::get(url).await?;
            let data = resp.bytes().await?;
            g.put(key, data.clone());
            data
        }
    };
    Ok(data)
}